package services

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"github.com/uber/tchannel-go/thrift"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/request"
	response2 "go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/app/ops/zclients"
	"go.didapinche.com/foundation/plat-echo/pkg/constants"
	"go.didapinche.com/foundation/plat-echo/pkg/consul"
	"go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/goapi/plat_limos_rpc"
	"go.didapinche.com/goapi/plat_rocket_api"
	"go.didapinche.com/goapi/user_department_service_api"
	"go.didapinche.com/time"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"sort"
	"strconv"
	"strings"
	"sync"
	time2 "time"
)

type TemplateService interface {
	Create(ctx context.Context, t *models.Template) error
	Update(ctx context.Context, t *models.Template) error
	Delete(ctx context.Context, id uint64) error
	FindByAppID(ctx context.Context, appID uint64) ([]*models.Template, error)   //获得某个app的全部模版
	FindByApiID(ctx context.Context, apiID uint64) ([]*models.Template, error)   //获得某个api的全部模版
	FindByHostID(ctx context.Context, hostID uint64) ([]*models.Template, error) //获得某个host的全部模版

	Find(ctx context.Context, name string, typeID, kind uint64, pageSize, pageNum int) ([]*response2.TemplateResp, int64, error) //通过模版分类id和类型查询
	//预览
	Preview(ctx context.Context, t *models.Template) (*models.Graph, error)
	FindUpdateApp(ctx context.Context, req *request.TempUpdate) ([]*models.Application, int64, error)   //查询被覆盖或者没有被覆盖的app
	FindUpdateApi(ctx context.Context, req *request.TempUpdate) ([]*models.Api, int64, error)           //查询被覆盖或者没有被覆盖的api
	FindUpdateHost(ctx context.Context, req *request.TempUpdate) ([]*models.HostResource, int64, error) //查询被覆盖或者没有被覆盖的host
	UpdateK8s(ctx context.Context, id, kind int, name string) error
	FindSla(ctx context.Context) ([]*response2.Metric, error)       //查询强制订阅的
	Subscribe(ctx context.Context, req *request.SubscribeReq) error //模版订阅
	FindTempType(ctx context.Context) (map[uint64]string, error)
	ChangeTemplate(ctx context.Context, templates []*models.Template, id uint64, groups map[string]string) []*models.Graph
	GetNodeByGroup(ctx context.Context) map[string]string
	GetProgress(ctx context.Context) ([]*response2.GraphProgress, error)
	ReplaceExpressionByApp(ctx context.Context, app *models.Application, templates []*models.Template)
	//搜索预览的app，API，host
	FindByKey(ctx context.Context, kind models.GraphKind, keyword string, page int, size int) ([]*models.AccessRecord, int64, error)
}

//todo
type templateService struct {
	db                 *gorm.DB
	logger             *zap.Logger
	k8s                K8sService
	repository         repositories.TemplateRepository
	graphRepository    repositories.GraphRepository
	tempTypeRepository repositories.TempTypeRepository
	apiRepository      repositories.ApiRepository
	appRepository      repositories.ApplicationRepository
	hostRepository     repositories.HostRepository
	dingdingReposotory repositories.DingdingRepository
	limos              plat_limos_rpc.TChanLimosService
	rocket             plat_rocket_api.TChanRocketMqService
	graphProgress      *response2.GraphProgress
	progress           []*response2.GraphProgress
	department         user_department_service_api.TChanDepartmentService
	syn1               sync.Mutex //1锁正在运行
	syn2               sync.Mutex //2锁返回数组
	consul             *consul.Consul
}

func NewTemplateService(
	db *gorm.DB,
	graphRepository repositories.GraphRepository,
	tempTypeRepository repositories.TempTypeRepository,
	logger *zap.Logger,
	consul *consul.Consul,
	department user_department_service_api.TChanDepartmentService,
	k8s K8sService,
	dingdingReposotory repositories.DingdingRepository,
	repository repositories.TemplateRepository,
	limos plat_limos_rpc.TChanLimosService,
	rocket plat_rocket_api.TChanRocketMqService,
	apiRepository repositories.ApiRepository,
	hostRepository repositories.HostRepository,
	appRepository repositories.ApplicationRepository,
) TemplateService {
	return &templateService{
		tempTypeRepository: tempTypeRepository,
		db:                 db,
		department:         department,
		consul:             consul,
		dingdingReposotory: dingdingReposotory,
		graphRepository:    graphRepository,
		hostRepository:     hostRepository,
		logger:             logger.With(zap.String("type", "service.templateService")),
		k8s:                k8s,
		apiRepository:      apiRepository,
		repository:         repository,
		rocket:             rocket,
		limos:              limos,
		appRepository:      appRepository,
		graphProgress:      &response2.GraphProgress{},
		progress:           make([]*response2.GraphProgress, 0),
	}
}

func (s *templateService) FindSla(ctx context.Context) ([]*response2.Metric, error) {
	templates, err := s.repository.FindSla(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "call tempTypeRepository FindSla error")
	}
	metrics := make([]*response2.Metric, 0)
	for _, t := range templates {
		if !t.Alert.ValidateAlert() {
			continue
		}
		metric := new(response2.Metric)
		metric.Name = t.Name
		metric.Show = t.Sla
		metric.Key = strconv.Itoa(int(t.ID))
		metrics = append(metrics, metric)
	}
	return metrics, nil
}

func (s *templateService) FindTempType(ctx context.Context) (map[uint64]string, error) {
	temptype, err := s.tempTypeRepository.FindAll(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "call tempTypeRepository FindAll error")
	}
	m := make(map[uint64]string)
	for _, t := range temptype {
		m[t.ID] = t.Name
	}
	return m, nil
}

//模版强制订阅
func (s *templateService) Subscribe(ctx context.Context, req *request.SubscribeReq) error {
	template, err := s.repository.FindByID(ctx, req.ID)
	if err != nil {
		s.logger.Error("call templateService.FindByID error", zap.Error(err))
		return errors.Wrap(err, "call templateService.FindByID error")
	}
	template.SubscribeInfo = &models.SubscribeInfo{SubscribeType: req.SubscribeType, Owner: req.Owner}
	if req.Condition != nil {
		if req.Condition.Op == "contain" {
			template.SubscribeInfo.Contain = true
		}
		if req.Condition.Key == "level" {
			template.SubscribeInfo.Level = req.Condition.Value
		}
	}
	if err := s.repository.Update(ctx, template); err != nil {
		s.logger.Error("call templateService.Save error", zap.Error(err))
		return errors.Wrap(err, "call templateService.Save error")
	}
	if err := s.graphRepository.UpdateIsForceByTemplateID(ctx, template.ID, req.SubscribeType, template.SubscribeInfo.Level, template.SubscribeInfo.Contain); err != nil {
		s.logger.Error("call graphRepository.UpdateIsForceByTemplateID error", zap.Error(err))
		return errors.Wrap(err, "call graphRepository.UpdateIsForceByTemplateID error")
	}

	return nil
}

func (s *templateService) FindUpdateApp(ctx context.Context, req *request.TempUpdate) ([]*models.Application, int64, error) {
	template, err := s.repository.FindByID(ctx, req.TempID)
	if err != nil {
		s.logger.Error("call templateService.FindByID error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.FindByID error")
	}
	apps, count, err := s.repository.FindUpdateApp(ctx, template.ID, req.Name, req.PageNum, req.PageSize, req.Eq)
	if err != nil {
		s.logger.Error("call templateService.FindUpdateApp error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.FindUpdateApp error")
	}
	return apps, count, nil
}

func (s *templateService) FindUpdateApi(ctx context.Context, req *request.TempUpdate) ([]*models.Api, int64, error) {
	template, err := s.repository.FindByID(ctx, req.TempID)
	if err != nil {
		s.logger.Error("call templateService.FindByID error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.FindByID error")
	}
	apis, count, err := s.repository.FindUpdateApi(ctx, template.ID, req.Name, req.PageNum, req.PageSize, req.Eq)
	if err != nil {
		s.logger.Error("call templateService.FindUpdateApi error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.FindUpdateApi error")
	}
	for _, a := range apis {
		if a.HttpMethod != "" {
			a.TemplateType = 1
		} else {
			a.TemplateType = 2
		}
	}
	return apis, count, nil
}

func (s *templateService) FindUpdateHost(ctx context.Context, req *request.TempUpdate) ([]*models.HostResource, int64, error) {
	template, err := s.repository.FindByID(ctx, req.TempID)
	if err != nil {
		s.logger.Error("call templateService.FindByID error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.FindByID error")
	}
	hosts, count, err := s.repository.FindUpdateHost(ctx, template.ID, req.Name, req.PageNum, req.PageSize, req.Eq)
	if err != nil {
		s.logger.Error("call templateService.FindUpdateHost error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.FindUpdateHost error")
	}
	return hosts, count, nil
}

// 创建模版
func (s *templateService) Create(ctx context.Context, t *models.Template) error {

	if err := s.repository.Create(ctx, t); err != nil {
		return errors.Wrap(err, "call template.Cteate error")
	}

	// 将模版同步到对应到图中
	go func() { s.RefreshTemplate(context.Background(), t, 0) }()

	return nil
}

func (s *templateService) Delete(ctx context.Context, id uint64) error {
	template, _ := s.repository.FindByID(ctx, id)
	if err := s.repository.Delete(ctx, id); err != nil {
		s.logger.Error("call template.Delete error", zap.Error(err))
		return errors.Wrap(err, "call template.Delete error")
	}
	if err := s.graphRepository.DeleteByTemplateID(ctx, id); err != nil {
		s.logger.Error("call graphRepository.DeleteByTemplateID error", zap.Error(err))
		return errors.Wrap(err, "call graphRepository.DeleteByTemplateID error")
	}
	template.Type = 0
	//判断类型对k8s资源进行更改
	go func() { s.RefreshTemplate(context.Background(), template, 2) }()

	return nil
}

func (s *templateService) Update(ctx context.Context, t *models.Template) error {
	template, err := s.repository.FindByID(ctx, t.ID)
	if err != nil {
		return errors.Wrap(err, "call template.FindByID error")
	}
	if template.Alert != nil {
		t.OldVlaue = template.Alert.Threshold
	}
	if template.Alert != nil && t.Alert != nil {

		if template.Alert.Threshold == t.Alert.Threshold {
			t.Type = 2
		}
	}
	if err := s.repository.Update(ctx, t); err != nil {
		s.logger.Error("call template.Save error", zap.Error(err))
		return errors.Wrap(err, "call template.Save error")
	}
	//判断类型对k8s资源进行更改
	go func() { s.RefreshTemplate(context.Background(), t, 1) }()

	return nil
}

func (s *templateService) Find(ctx context.Context, name string, typeID, kind uint64, pageSize, pageNum int) ([]*response2.TemplateResp, int64, error) {
	templates, count, err := s.repository.Find(ctx, name, typeID, kind, pageSize, pageNum)
	if err != nil {
		s.logger.Error("call templateService.Find error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateService.Find error")
	}
	temptype, err := s.FindTempType(ctx)
	for _, t := range templates {
		t.TypeName = temptype[t.TypeID]
	}
	sort.Sort(models.TemplateSlice(templates))
	return response2.NewTemplateResps(templates), count, nil
}

//查询某AppID的告警
func (s *templateService) FindByAppID(ctx context.Context, appID uint64) ([]*models.Template, error) {
	app, err := s.appRepository.FindByID(ctx, appID)
	if err != nil {
		s.logger.Error("call appRepository.FindByID error", zap.Error(err))
	}
	templates, err := s.repository.FindAlertAll(ctx, 0)
	if err != nil {
		s.logger.Error("call TemplateRepository.FindAlertAll error", zap.Error(err))
	}
	for i := 0; i < len(templates); {
		if app.Language == "java" {
			if strings.Contains(strings.ToUpper(templates[i].Name), "GO") {
				templates = append(templates[:i], templates[i+1:]...)
			}
		}
		if app.Language == "go" {
			if strings.Contains(strings.ToUpper(templates[i].Name), "JAVA") {
				templates = append(templates[:i], templates[i+1:]...)
			}
		}
		i++
	}
	s.ReplaceExpressionByApp(ctx, app, templates)
	return templates, nil
}

//查询某ApiID的告警
func (s *templateService) FindByApiID(ctx context.Context, apiID uint64) ([]*models.Template, error) {
	api, err := s.apiRepository.FindByID(ctx, apiID)
	if err != nil {
		s.logger.Error("call apiRepository.FindByID error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiRepository.FindByID error")
	}
	//获取api的所有模版
	templates, err := s.repository.FindAlertAll(ctx, 1)
	if err != nil {
		s.logger.Error("call templateRepository.FindAlertAll error", zap.Error(err))
		return nil, errors.Wrap(err, "call templateRepository.FindAlertAll error")
	}
	if api.Type == 0 {
		for i := 0; i < len(templates); {
			if templates[i].TypeID == constants.ZeusClientTempType || templates[i].TypeID == constants.ZeusServerTempType {
				templates = append(templates[:i], templates[i+1:]...)
				continue
			}
			i++
		}
	} else if api.Type == 1 {
		for i := 0; i < len(templates); {
			if templates[i].TypeID == constants.ZeusClientTempType || templates[i].TypeID == constants.ApiHttpTempType {
				templates = append(templates[:i], templates[i+1:]...)
				continue
			}
			i++
		}
	} else if api.Type == 2 {
		for i := 0; i < len(templates); {
			if templates[i].TypeID == constants.ApiHttpTempType || templates[i].TypeID == constants.ZeusServerTempType {
				templates = append(templates[:i], templates[i+1:]...)
				continue
			}
			i++
		}
	}
	s.ReplaceExpressionByApi(ctx, api, templates)
	return templates, nil
}

//查询某HostID的告警
func (s *templateService) FindByHostID(ctx context.Context, hostID uint64) ([]*models.Template, error) {
	host, err := s.hostRepository.FindByID(ctx, hostID)
	if err != nil {
		s.logger.Error("call apiRepository.FindByID error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiRepository.FindByID error")

	}
	//获取api的所有强制订阅
	templates, err := s.repository.FindAlertAll(ctx, 2)
	if err != nil {
		s.logger.Error("call TemplateRepository.FindAlertAll error", zap.Error(err))
		return nil, errors.Wrap(err, "call TemplateRepository.FindAlertAll error")

	}
	s.ReplaceExpressionByHost(ctx, host, templates)
	return templates, nil
}

//作用是将刷新k8s报警规则并将模版同步到图,operation操作类型0：增加 1：修改 2删除
func (s *templateService) RefreshTemplate(ctx context.Context, template *models.Template, operation int) {
	defer func() {
		//todo
		if r := recover(); r != nil {
			s.logger.Error("recover RefreshTemplate")
			fmt.Println(r)
		}
	}()
	nodes := s.GetNodeByGroup(ctx)
	progress := new(response2.GraphProgress)
	progress.ID = template.ID
	progress.Status = 1
	if s.graphProgress != nil {
		if s.graphProgress.Status == 1 {
			progress.Status = 0
		}
	}
	s.syn2.Lock()
	s.progress = append(s.progress, progress)
	s.syn2.Unlock()
	s.syn1.Lock()
	defer s.syn1.Unlock()
	s.graphProgress = new(response2.GraphProgress)
	s.graphProgress.Status = 1
	s.graphProgress.ID = template.ID
	//type 0:覆盖全部  1：覆盖已经更新 2：覆盖未更新
	//app增加模版，覆盖全部
	if template.Kind == models.AppTemplate {
		apps, err := s.appRepository.FindAll(ctx)
		if err != nil {
			s.logger.Error("call templateService RefreshTemplate error:s.appRepository.FindAll(ctx)", zap.Error(err))
			s.graphProgress.Status = 2
			s.graphProgress = nil
		}
		s.graphProgress.Total = len(apps)
		for _, app := range apps {
			templates := make([]*models.Template, 0)
			templates = append(templates, template.Copy())
			s.ReplaceExpressionByApp(ctx, app, templates)
			graph := s.MakeGraph(ctx, app.ID, templates[0], nodes)
			if operation == 2 {
				s.graphProgress.Current++
				s.UpdateK8s(ctx, int(app.ID), 0, app.Name)
				continue
			}
			if operation == 1 {
				graph2, err := s.graphRepository.FindByTemplateIDAndIDAndKind(ctx, template.ID, app.ID, 0)
				if err == nil {
					graph.ID = graph2.ID
					if template.Type == 2 {
						if graph2.Alert != nil {
							if graph2.Alert.Threshold != template.OldVlaue {
								if graph.Alert != nil {
									graph.Alert.Threshold = graph2.Alert.Threshold
								}
							}
						}
					} else if template.Type == 1 {
						if graph2.Alert != nil {
							if graph2.Alert.Threshold == template.OldVlaue {
								if graph.Alert != nil {
									graph.Alert.Threshold = graph2.Alert.Threshold
								}
							}
						}
					}
				}
			}
			if _, err := s.graphRepository.Save(ctx, graph); err != nil {
				s.logger.Error("call templateService RefreshTemplate error:s.graphRepository.Creates", zap.Error(err))
				continue
			}
			s.UpdateK8s(ctx, int(app.ID), 0, app.Name)
			s.graphProgress.Current++
		}
		s.graphProgress = nil

		//api增加模版，覆盖全部
		//todo api template
	} else if template.Kind == models.ApiTemplate {
		templates, err := s.repository.FindAlertAll(ctx, 1)
		if err == nil {
			s.k8s.UpdateAlertResourceByTemplate(ctx, "api", templates)
		}
		apis, err := s.apiRepository.FindAll(ctx)
		s.graphProgress.Total = len(apis)
		if err != nil {
			s.logger.Error("call templateService RefreshTemplate error:s.appRepository.FindAll(ctx)", zap.Error(err))
			s.graphProgress.Status = 2
			s.graphProgress = nil
		}
		for _, api := range apis {
			if api.Type == 0 && (template.TypeID == constants.ZeusServerTempType || template.TypeID == constants.ZeusClientTempType) {
				s.graphProgress.Current++
				continue
			}
			if api.Type == 1 && (template.TypeID == constants.ApiHttpTempType || template.TypeID == constants.ZeusClientTempType) {
				s.graphProgress.Current++
				continue
			}
			if api.Type == 2 && (template.TypeID == constants.ApiHttpTempType || template.TypeID == constants.ZeusServerTempType) {
				s.graphProgress.Current++
				continue
			}

			if operation == 2 {
				s.graphProgress.Current++
				continue
			}
			templates := make([]*models.Template, 0)
			templates = append(templates, template.Copy())
			s.ReplaceExpressionByApi(ctx, api, templates)

			graph := s.MakeGraph(ctx, api.ID, templates[0], nodes)
			if operation == 1 {
				graph2, err := s.graphRepository.FindByTemplateIDAndIDAndKind(ctx, template.ID, api.ID, 1)
				if err == nil {
					graph.ID = graph2.ID
					if template.Type == 2 {
						if graph2.Alert != nil {
							if graph2.Alert.Threshold != template.OldVlaue {
								if graph.Alert != nil {
									graph.Alert.Threshold = graph2.Alert.Threshold
								}
							}
						}
					} else if template.Type == 1 {
						if graph2.Alert != nil {
							if graph2.Alert.Threshold == template.OldVlaue {
								if graph.Alert != nil {
									graph.Alert.Threshold = graph2.Alert.Threshold
								}
							}
						}
					}
				}
			}

			if _, err := s.graphRepository.Save(ctx, graph); err != nil {
				s.logger.Error("call templateService RefreshTemplate error:s.graphRepository.Creates", zap.Error(err))
				continue
			}
			//s.UpdateK8s(ctx, int(api.ID), 1, "api-"+strconv.Itoa(int(api.ID)))
			s.graphProgress.Current++
		}
		s.graphProgress = nil

	} else if template.Kind == models.HostTemplate {
		hosts, err := s.hostRepository.FindAll(ctx)
		if err != nil {
			s.logger.Error("call templateService RefreshTemplate error:s.hostRepository.FindAll(ctx)", zap.Error(err))
			s.graphProgress.Status = 2
			s.graphProgress = nil
		}
		s.graphProgress.Total = len(hosts)
		for _, host := range hosts {
			if operation == 2 {
				s.graphProgress.Current++
				//	s.UpdateK8s(ctx, int(host.ID), 2, host.IP)
				continue
			}
			templates := make([]*models.Template, 0)
			templates = append(templates, template.Copy())
			s.ReplaceExpressionByHost(ctx, host, templates)
			graph := s.MakeGraph(ctx, host.ID, templates[0], nodes)
			if operation == 1 {
				graph2, err := s.graphRepository.FindByTemplateIDAndIDAndKind(ctx, template.ID, host.ID, 2)
				if err == nil {
					graph.ID = graph2.ID
					if template.Type == 2 {
						if graph2.Alert != nil {
							if graph2.Alert.Threshold != template.OldVlaue {
								if graph.Alert != nil {
									graph.Alert.Threshold = graph2.Alert.Threshold
								}
							}
						}
					} else if template.Type == 1 {
						if graph2.Alert != nil {
							if graph2.Alert.Threshold == template.OldVlaue {
								if graph.Alert != nil {
									graph.Alert.Threshold = graph2.Alert.Threshold
								}
							}
						}
					}
				}
			}
			if _, err := s.graphRepository.Save(ctx, graph); err != nil {
				s.logger.Error("call templateService RefreshTemplate error:s.graphRepository.Creates", zap.Error(err))
				continue
			}
			//s.UpdateK8s(ctx, int(host.ID), 2, host.IP)
			s.graphProgress.Current++
		}
		s.graphProgress = nil
	}

	s.syn2.Lock()
	for i, p := range s.progress {
		if p.ID == template.ID {
			if p.Status == 3 {
				s.progress[i].Status = 3
				break
			}
			s.progress = append(s.progress[:i], s.progress[i+1:]...)
			break
		}
	}
	s.syn2.Unlock()
}

//作用是将app级别表达式上的{{.***}}替换为实际***
func (s *templateService) ReplaceExpressionByApp(ctx context.Context, app *models.Application, templates []*models.Template) {
	ips, _ := zclients.FindIP(ctx, s.limos, app.ID, viper.GetString("app.env"))
	m := s.consul.GetZeusMap()

	_, group, _ := zclients.FindGroup(ctx, app.Name, s.rocket)
	pid, _ := zclients.FindPid(ctx, s.limos, app.ID, viper.GetString("app.env"))
	for i, _ := range templates {
		if templates[i].Alert != nil && templates[i].Alert.Expression != "" {
			if strings.Contains(templates[i].Alert.Expression, "{{.AppName}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.AppName}}", app.Name)
			}
			if strings.Contains(templates[i].Alert.Expression, "{{.PID}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.PID}}", pid)
			}
			if strings.Contains(templates[i].Alert.Expression, "{{.Services}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.Services}}", m[app.Name])
			}
			if strings.Contains(templates[i].Alert.Expression, "{{.IP}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.IP}}", ips)
			}
			if strings.Contains(templates[i].Alert.Expression, "{{.Group}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.Group}}", group)
			}
		}
		if templates[i].Queries != nil && len(templates[i].Queries) > 0 {
			for j, _ := range templates[i].Queries {
				if strings.Contains(templates[i].Queries[j].Expression, "{{.AppName}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.AppName}}", app.Name)
				}
				if strings.Contains(templates[i].Queries[j].Expression, "{{.PID}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.PID}}", pid)
				}
				if strings.Contains(templates[i].Queries[j].Expression, "{{.Services}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.Services}}", m[app.Name])
				}
				if strings.Contains(templates[i].Queries[j].Expression, "{{.IP}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.IP}}", ips)
				}
				if strings.Contains(templates[i].Queries[j].Expression, "{{.Group}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.Group}}", group)
				}
			}
		}
	}
}

//作用是将api级别报警表达式上的{{.***}}替换为实际***
func (s *templateService) ReplaceExpressionByApi(ctx context.Context, api *models.Api, templates []*models.Template) {

	for i, _ := range templates {
		if templates[i].Alert != nil || templates[i].Alert.Expression != "" {
			if strings.Contains(templates[i].Alert.Expression, "{{.AppName}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.AppName}}", api.AppName)
			}
			if strings.Contains(templates[i].Alert.Expression, "{{.Uri}}") {
				templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.Uri}}", api.HttpPath)
			}
			if api.Type == 0 {
				if strings.Contains(templates[i].Alert.Expression, "{{.Method}}") {
					templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.Method}}", api.HttpMethod)
				}
			} else {
				if strings.Contains(templates[i].Alert.Expression, "{{.Method}}") {
					templates[i].Alert.Expression = strings.ReplaceAll(templates[i].Alert.Expression, "{{.Method}}", api.ZeusMethod)
				}
			}
		}
		if templates[i].Queries != nil || len(templates[i].Queries) > 0 {
			for j, _ := range templates[i].Queries {
				if strings.Contains(templates[i].Queries[j].Expression, "{{.AppName}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.AppName}}", api.AppName)
				}
				if strings.Contains(templates[i].Queries[j].Expression, "{{.Uri}}") {
					templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.Uri}}", api.HttpPath)
				}
				if api.Type == 0 {
					if strings.Contains(templates[i].Queries[j].Expression, "{{.Method}}") {
						templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.Method}}", api.HttpMethod)
					}
				} else {
					if strings.Contains(templates[i].Queries[j].Expression, "{{.Method}}") {
						templates[i].Queries[j].Expression = strings.ReplaceAll(templates[i].Queries[j].Expression, "{{.Method}}", api.ZeusMethod)
					}
				}
			}
		}
	}
}

//作用是将host级别报警表达式上的{{.ip}}替换为实际ip
func (s *templateService) ReplaceExpressionByHost(ctx context.Context, host *models.HostResource, templates []*models.Template) {
	for _, t := range templates {
		if t.Alert != nil || t.Alert.Expression != "" {
			if strings.Contains(t.Alert.Expression, "{{.IP}}") {
				t.Alert.Expression = strings.ReplaceAll(t.Alert.Expression, "{{.IP}}", host.IP)
			}
			if strings.Contains(t.Alert.Expression, "{{.Host}}") {
				t.Alert.Expression = strings.ReplaceAll(t.Alert.Expression, "{{.Host}}", host.HostName)
			}
		}
		if t.Queries != nil || len(t.Queries) > 0 {
			for i, _ := range t.Queries {
				if host.IP != "" {
					if strings.Contains(templates[i].Queries[i].Expression, "{{.IP}}") {
						t.Queries[i].Expression = strings.ReplaceAll(t.Queries[i].Expression, "{{.IP}}", host.IP)
					}
				}
				if host.HostName != "" {
					if strings.Contains(t.Queries[i].Expression, "{{.Host}}") {
						t.Queries[i].Expression = strings.ReplaceAll(t.Queries[i].Expression, "{{.Host}}", host.HostName)
					}
				}
			}
		}
	}
}

//name是规则名，可以用appName，apiID，dashboardName，ip表示
func (s *templateService) UpdateK8s(ctx context.Context, id, kind int, name string) error {
	graphs, err := s.graphRepository.FindByKindAndID(ctx, id, kind)
	if err != nil {
		s.logger.Error("call GraphRepository FindByKindAndID error", zap.Error(err))
		return errors.Wrap(err, "call GraphRepository FindByKindAndID error")
	}

	s.k8s.UpdateAlertResource(ctx, name, graphs)
	return nil
}

func (s templateService) MakeGraph(ctx context.Context, id uint64, t *models.Template, nodes map[string]string) *models.Graph {
	graph := new(models.Graph)
	group := ""
	level := 0
	owners1 := make([]*models.ApplicationOwner, 0)
	graph.Kind = t.Kind
	if t.Kind == models.AppTemplate {
		app, err := s.appRepository.FindByID(ctx, id)
		if err == nil {
			level = app.Level
			owners1 = app.GetOwners()
			group = app.DevGroupName
		}
		graph.AppID = id
	} else if t.Kind == models.ApiTemplate {
		api, err := s.apiRepository.FindByID(ctx, id)
		if err == nil {
			level = api.Level
			owners1 = api.GetOwners()
			group = api.DevGroupName
		}
		graph.ApiID = id
	} else if t.Kind == models.HostTemplate {
		graph.HostID = id
	}
	graph.Level = level
	graph.Name = t.Name
	graph.Queries = t.Queries
	graph.TemplateId = t.ID
	graph.Order = t.Order
	graph.TypeID = t.TypeID
	graph.Alert = t.Alert
	if t.Alert.ValidateAlert() {
		receiver := new(models.Receiver)
		for _, o := range owners1 {
			user := new(models.User)
			user.ID = o.UserID
			user.Name = o.UserName
			receiver.Users = append(receiver.Users, user)

		}
		dingding := new(models.DingDingInfo)
		for {
			d, err := s.dingdingReposotory.FindByLevelAndGroup(ctx, level, group)
			if err != nil {
				break
			}

			if d.Token == "" {
				group = nodes[group]
			} else {
				dingding.Token = d.Token
				dingding.Name = d.Name
				break
			}
			if group == "" {
				break
			}
		}
		receiver.Dingdings = append(receiver.Dingdings, dingding)
		graph.Alert.Receiver = receiver
	}
	graph.General = t.General
	graph.Visualization = t.Visualization
	if t.SubscribeInfo != nil && t.SubscribeInfo.SubscribeType != nil && len(t.SubscribeInfo.SubscribeType) > 0 {
		if t.SubscribeInfo.Owner {
			if t.SubscribeInfo.Contain {
				if data.ContainInt(t.SubscribeInfo.Level, level) {
					subscribe := new(models.Subscribe)
					owners2 := make([]string, 0)
					for _, o := range owners1 {
						owners2 = append(owners2, o.UserID)
					}
					subscribe.SubscribeForce = t.SubscribeInfo.SubscribeType
					graph.Subscribe = subscribe
					graph.IsForce = true
				}
			} else if !t.SubscribeInfo.Contain {
				if !data.ContainInt(t.SubscribeInfo.Level, level) {
					subscribe := new(models.Subscribe)
					owners2 := make([]string, 0)
					for _, o := range owners1 {
						owners2 = append(owners2, o.UserID)
					}
					subscribe.SubscribeForce = t.SubscribeInfo.SubscribeType
					graph.Subscribe = subscribe
					graph.IsForce = true
				}
			}
		}
	}
	graph.CreateTime = time.Now()
	graph.UpdateTime = time.Now()
	return graph
}

func (s templateService) ChangeTemplate(ctx context.Context, templates []*models.Template, id uint64, groups map[string]string) []*models.Graph {
	graphs := make([]*models.Graph, 0)
	for _, t := range templates {
		graph := s.MakeGraph(ctx, id, t, groups)
		graphs = append(graphs, graph)
	}
	return graphs
}

func (s templateService) GetNodeByGroup(ctx context.Context) map[string]string {
	ctx2, _ := context.WithTimeout(ctx, 5*time2.Second)
	context := thrift.Wrap(ctx2)
	node, err := s.department.FindAllNodes(context)
	if err != nil {
		s.logger.Error("call userService.FindGroupsOfDevelopment error")
		return nil
	}
	nodes := zclients.GetMap(node)
	return nodes
}

func (s *templateService) GetProgress(ctx context.Context) ([]*response2.GraphProgress, error) {
	s.syn2.Lock()
	for i, p := range s.progress {
		if p.Status == 3 {
			graphs, _ := s.graphRepository.FindByTemplateID(ctx, p.ID)
			if len(graphs) == 0 {
				s.progress = append(s.progress[:i], s.progress[i+1:]...)
			}
		}
	}
	progress := s.progress
	for i, p := range progress {
		if s.graphProgress != nil {
			if p.ID == s.graphProgress.ID {
				progress[i] = s.graphProgress
			}
		}
	}
	s.syn2.Unlock()
	return progress, nil
}

func (s *templateService) Preview(ctx context.Context, t *models.Template) (*models.Graph, error) {
	templates := make([]*models.Template, 0)
	nodes := s.GetNodeByGroup(ctx)
	templates = append(templates, t)
	if t.Kind == 0 {
		app, err := s.appRepository.FindByID(ctx, t.AppID)
		if err != nil {
			app, err = s.appRepository.FindByID(ctx, viper.GetUint64("preview.AppID"))
			if err != nil {
				return nil, errors.Wrap(err, "call appRepository.FindByID error")
			}
		}
		s.ReplaceExpressionByApp(ctx, app, templates)
		graph := s.MakeGraph(ctx, app.ID, t, nodes)
		graph.AppID = app.ID
		graph.AppName = app.Name
		return graph, nil

	} else if t.Kind == 1 {
		api, err := s.apiRepository.FindByID(ctx, t.ApiID)
		if err != nil {
			api, err = s.apiRepository.FindByID(ctx, viper.GetUint64("preview.ApiID"))
			if err != nil {
				return nil, errors.Wrap(err, "call apiRepository.FindByID error")
			}
		}
		s.ReplaceExpressionByApi(ctx, api, templates)
		graph := s.MakeGraph(ctx, api.ID, t, nodes)
		graph.AppID = api.ID
		graph.AppName = api.AppName
		graph.ApiInfo = api
		return graph, nil
	} else if t.Kind == 2 {
		host, err := s.hostRepository.FindByID(ctx, t.HostID)
		if err != nil {
			host, err = s.hostRepository.FindByID(ctx, viper.GetUint64("preview.HostID"))
			if err != nil {
				return nil, errors.Wrap(err, "call appRepository.FindByID error")
			}
		}
		s.ReplaceExpressionByHost(ctx, host, templates)
		graph := s.MakeGraph(ctx, host.ID, t, nodes)
		return graph, nil
	}
	return nil, nil
}
func (s *templateService) FindByKey(ctx context.Context, kind models.GraphKind, keyword string, page int, size int) ([]*models.AccessRecord, int64, error) {
	var accessRecords []*models.AccessRecord
	var total int64
	switch kind {
	case models.AppTemplate:
		apps, count, err := s.appRepository.FindByNameAndGroupForPage(ctx, keyword, "", 0, size, page)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepo.FindByNameAndGroupForPage err")
		}
		for _, app := range apps {
			accessRecords = append(accessRecords, &models.AccessRecord{
				ID:   app.ID,
				Name: app.Name,
			})
		}
		total = count
	case models.ApiTemplate:
		apiInfos, count, err := s.apiRepository.FindByQuery(ctx, keyword, page, size)
		if err != nil {
			return nil, 0, errors.Wrap(err, "call appRepo.FindByNameAndGroupForPage err")
		}
		for _, info := range apiInfos {
			accessRecords = append(accessRecords, &models.AccessRecord{
				ID:   info.ID,
				Name: info.GetMethod(),
			})
		}
		total = count
	case models.HostTemplate:
		hosts, count, err := s.hostRepository.FindByEnvIDAndGroupAndDCAndNameAndLikeName(ctx, 3, "", "", "", keyword, uint64(page), uint64(size))
		if err != nil {
			return nil, 0, errors.Wrap(err, "call hostRepo.FindByEnvIDAndGroupAndDCAndNameAndLikeName err")
		}
		for _, host := range hosts {
			accessRecords = append(accessRecords, &models.AccessRecord{
				Name: host.HostName,
				IP:   host.IP,
				ID:   host.ID,
			})
		}
		total = count
	}
	return accessRecords, total, nil
}
